import os
import sys
import threading
import time
from queue import Queue

from PyQt5 import QtCore
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QWidget, QPushButton, \
    QHBoxLayout, QVBoxLayout, QComboBox, QTextBrowser, \
    QLabel, QGridLayout, QMessageBox, QMainWindow

from function.serial_analysis import analysis, initExcel
from function.serial_port import SerialPort, get_serial
from ui.qt_analysis import AnalysisUi
from ui.qt_database import DatabaseUi


def get_resource_path(relative_path):
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS, relative_path)
    return os.path.join(os.path.abspath("."), relative_path)


class SetUi(QMainWindow):
    serial_select = QtCore.pyqtSignal(list)
    serial_err = QtCore.pyqtSignal(str)
    __connectBuf = False  # 串口开启标志
    __analysisUiBuf = True  # 子窗口开启标志
    __queue = Queue(1024)  # 串口数据缓冲队列

    # __clear_buf = 0

    def __init__(self):
        super().__init__()
        self.__connectBuf = False
        self.__textThread = threading.Thread(target=self.__textUpdateThread)
        self.__textThread.setDaemon(True)
        # self.__clear_buf = 0
        self.widget = QWidget()

        # 整体布局
        # 主垂直布局
        self.vbox = QVBoxLayout()
        # 上层水平布局
        self.top_hbox = QHBoxLayout()
        # 下层水平布局
        self.down_hbox = QHBoxLayout()
        # # 上层左侧垂直布局
        self.left_top_vbox = QVBoxLayout()
        # 上层左侧网格布局
        self.left_top_Gbox = QGridLayout()

        # 上层左侧控件
        # 打开串口按钮
        self.openButton = QPushButton("打开串口")
        # 格式选择按钮
        self.selectFormat = QPushButton("解析格式选择")
        self.selectDatabase = QPushButton("数据库选择")
        # 串口选择窗口
        self.select1 = QComboBox()
        self.select2 = QComboBox()
        self.select3 = QComboBox()
        self.select4 = QComboBox()
        self.select5 = QComboBox()
        self.select6 = QComboBox()
        self.left_label1 = QLabel("串口")
        self.left_label2 = QLabel("波特率")
        self.left_label3 = QLabel("数据位")
        self.left_label4 = QLabel("校验位")
        self.left_label5 = QLabel("停止位")
        self.left_label6 = QLabel("流控")

        # 上层右侧控件
        # 文本显示窗口
        # self.TextView = QTextEdit()
        self.TextView = QTextBrowser()

        # 下层控件
        # 保存按钮
        self.saveExport = QPushButton("导出数据")

        self.serialBuf = SerialPort()
        self.initUI()
        self.__runCheckSerial()  # 启动监测线程
        self.initSerial()
        self.book, self.sheet = initExcel()

    def initUI(self):
        # 显示任务栏图标
        if sys.platform == "win32":
            import ctypes
            ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("6666")

        # 设置窗口的标题
        self.setWindowTitle('Python QT 串口解析转存')
        self.resize(800, 500)
        # 设置窗口ICON
        self.setWindowIcon(QIcon(get_resource_path("image/星球.png")))

        self.left_top_vbox.setSpacing(15)
        self.top_hbox.setSpacing(15)
        self.down_hbox.setSpacing(15)

        self.left_top_vbox.addLayout(self.left_top_Gbox)
        self.top_hbox.addLayout(self.left_top_vbox)  # 添加左侧水平布局
        self.vbox.addLayout(self.top_hbox)  # 添加上层水平布局
        self.vbox.addLayout(self.down_hbox)  # 添加下层水平布局

        self.select1.addItems(get_serial())

        self.TextView.document().setMaximumBlockCount(150)

        select2_list = ["4800", "9600", "19200", "38400",
                        "57600", "74800", "115200", "230400",
                        "460800", "576000", "1152000"]
        self.select2.addItems(select2_list)
        self.select2.setCurrentIndex(6)
        select3_list = ["5", "6", "7", "8"]
        self.select3.addItems(select3_list)
        self.select3.setCurrentIndex(3)
        select4_list = ["None", "Even", "Mark", "Odd", "Space"]
        self.select4.addItems(select4_list)
        select5_list = ["One", "OPF", "Two"]
        self.select5.addItems(select5_list)
        select6_list = ["None", "XOnXOff"]
        self.select6.addItems(select6_list)

        self.left_top_Gbox.addWidget(self.left_label1, 0, 0)
        self.left_top_Gbox.addWidget(self.select1, 0, 1)
        self.left_top_Gbox.addWidget(self.left_label2, 1, 0)
        self.left_top_Gbox.addWidget(self.select2, 1, 1)
        self.left_top_Gbox.addWidget(self.left_label3, 2, 0)
        self.left_top_Gbox.addWidget(self.select3, 2, 1)
        self.left_top_Gbox.addWidget(self.left_label4, 3, 0)
        self.left_top_Gbox.addWidget(self.select4, 3, 1)
        self.left_top_Gbox.addWidget(self.left_label5, 4, 0)
        self.left_top_Gbox.addWidget(self.select5, 4, 1)
        self.left_top_Gbox.addWidget(self.left_label6, 5, 0)
        self.left_top_Gbox.addWidget(self.select6, 5, 1)
        self.left_top_vbox.addWidget(self.openButton)  # 左侧垂直布局2添加打开串口按钮
        self.left_top_vbox.addWidget(self.selectFormat)
        self.left_top_vbox.addWidget(self.selectDatabase)

        self.top_hbox.addWidget(self.TextView)  # 上层水平布局添加文本显示窗口
        self.down_hbox.addWidget(self.saveExport)  # 下层水平布局添加保存按钮

        # 按钮信号
        self.openButton.clicked.connect(self.buttonMsg)
        self.selectFormat.clicked.connect(self.buttonMsg)
        self.selectDatabase.clicked.connect(self.buttonMsg)
        self.saveExport.clicked.connect(self.buttonMsg)
        # 选择框信号
        self.select1.activated.connect(self.selectMsg)
        self.select2.activated.connect(self.selectMsg)
        self.select3.activated.connect(self.selectMsg)
        self.select4.activated.connect(self.selectMsg)
        self.select5.activated.connect(self.selectMsg)
        self.select6.activated.connect(self.selectMsg)
        # 串口选择更新信号
        self.serial_select.connect(self.uiUpdate)
        # 串口异常断开信号
        self.serial_err.connect(self.__serialAbnormal)

        self.left_top_vbox.addStretch()

        # self.setLayout(self.vbox)
        self.widget.setLayout(self.vbox)
        self.setCentralWidget(self.widget)

    # 按钮信号处理
    def buttonMsg(self):
        print("收到信号")
        if self.sender() == self.openButton:
            print(self.serialBuf.port)
            if self.serialBuf.port is None:
                print("串口为空")
                QMessageBox.information(self, "提示", "串口为空")
            else:
                if not self.__connectBuf:
                    if self.serialBuf.connect_serial():
                        self.__connectBuf = True
                        print("串口打开成功")
                        self.connectOnOff(False)
                        self.__runTextUpdateThread()
                    else:
                        print("串口打开失败")
                        QMessageBox.information(self, "提示", "串口打开失败")
                else:
                    self.__connectBuf = False
                    self.serialBuf.ser.close()
                    self.connectOnOff(True)
                    # self.uiUpdate(get_serial())
                    # self.initSerial()  # 关闭串口后重新刷新参数
                    print("串口关闭")
                    try:
                        self.book.save('./data.xls')
                    except:
                        print("excel保存失败")

        elif self.sender() == self.selectFormat:
            print("解析格式选择")
            self.__analysisUi()
        elif self.sender() == self.selectDatabase:
            print("数据库选择")
            self.__databaseUi()
        elif self.sender() == self.saveExport:
            print("导出数据")

    # 选择窗口信号处理
    def selectMsg(self):
        if self.sender() == self.select1:
            if self.select1.currentIndex() == -1:
                self.serialBuf.port = None
            else:
                self.serialBuf.port = self.select1.currentText()  # 获取选中端口名称
            print(self.serialBuf.port)
        elif self.sender() == self.select2:
            self.serialBuf.bps = self.select2.currentIndex()  # 获取选中索引
            print(self.serialBuf.bps)
        elif self.sender() == self.select3:
            self.serialBuf.byte_size = self.select3.currentIndex()
        elif self.sender() == self.select4:
            self.serialBuf.parity = self.select4.currentIndex()
        elif self.sender() == self.select5:
            self.serialBuf.stop_bits = self.select5.currentIndex()
        elif self.sender() == self.select6:
            self.serialBuf.flow_control = self.select6.currentIndex()

    # 刷新串口列表
    def uiUpdate(self, serial_list):
        # 串口开启之后界面不会刷新
        if not self.__connectBuf:
            self.select1.clear()
            self.select1.addItems(serial_list)
            self.select1.update()

    # 程序启动初始化参数
    def initSerial(self):
        if self.select1.currentIndex() != -1:
            self.serialBuf.port = self.select1.currentText()  # 获取选中端口名称
        self.serialBuf.bps = self.select2.currentIndex()  # 获取选中索引
        self.serialBuf.byte_size = self.select3.currentIndex()
        self.serialBuf.parity = self.select4.currentIndex()
        self.serialBuf.stop_bits = self.select5.currentIndex()
        self.serialBuf.flow_control = self.select6.currentIndex()
        print("端口:", self.serialBuf.port, "波特率:", self.serialBuf.bps,
              "数据位:", self.serialBuf.byte_size, "校验位:", self.serialBuf.parity,
              "停止位:", self.serialBuf.stop_bits, "流控:", self.serialBuf.flow_control)

    # 串口打开/关闭功能刷新
    def connectOnOff(self, o_f):
        self.select1.setEnabled(o_f)
        self.select2.setEnabled(o_f)
        self.select3.setEnabled(o_f)
        self.select4.setEnabled(o_f)
        self.select5.setEnabled(o_f)
        self.select6.setEnabled(o_f)
        if o_f:
            self.openButton.setText("打开串口")
        else:
            self.openButton.setText("关闭串口")

    # 检测串口接入与拔出
    def __checkSerialThread(self):
        serial_list = get_serial()
        while True:
            time.sleep(1)
            if serial_list != get_serial():
                if len(serial_list) > len(get_serial()):
                    print("串口拔出")
                else:
                    print("串口接入")
                serial_list = get_serial()
                print(serial_list)
                self.serial_select.emit(serial_list)

    # 读串口线程
    def __textUpdateThread(self):
        while self.__connectBuf:
            try:
                # 阻塞式读串口
                buf = self.serialBuf.ser.readline()
                # if self.__queue.full() is not True:
                #     self.__queue.put(buf)
                # if self.__queue.empty() is not True:
                #     buf_gbk = self.__queue.get(timeout=0).decode("GBK")
                #     self.TextView.append(buf_gbk)
                #     self.TextView.moveCursor(self.TextView.textCursor().End)  # 移动显示到底部
                #     print(buf_gbk)

                if buf is not None:
                    buf_gbk = buf.decode("GBK")
                    self.TextView.append(buf_gbk)
                    self.TextView.moveCursor(self.TextView.textCursor().End)  # 移动显示到底部
                    analysis(buf_gbk, self.sheet)
                    # print(buf_gbk)

            except Exception as e:
                self.serial_err.emit(e.args[0])  # 发送异常信号
                print(e.args[0])
                return 0

    # 刷屏线程
    def __refreshUiThread(self):
        while True:
            while self.__queue.empty() is not True:
                # if self.__queue.empty() is not True:
                buf_gbk = self.__queue.get().decode("GBK")
                self.TextView.append(buf_gbk)
                self.TextView.moveCursor(self.TextView.textCursor().End)  # 移动显示到底部
                print(buf_gbk)

    # 串口异常断开
    def __serialAbnormal(self, args):
        print(args)
        if args.find("拒绝访问"):
            if self.__connectBuf:
                print("串口异常断开")
                QMessageBox.critical(self, "错误", "串口异常关闭！")
                self.__connectBuf = False
                self.connectOnOff(True)
                self.serialBuf.ser.close()

    def __analysisUi(self):
        self.__analysisUiBuf = False
        print("解析格式选择窗口开启")
        # self.hide()  # 子窗口打开后隐藏主窗口
        analysis = AnalysisUi()
        analysis.exec()
        # analysis.show()
        # analysis.exec_()
        self.__analysisUiBuf = True
        analysis.close()
        self.show()  # 子窗口关闭后重新显示主窗口
        print("解析格式选择窗口关闭")

    def __databaseUi(self):
        # self.__analysisUiBuf = False
        print("数据库选择窗口开启")
        # self.hide()  # 子窗口打开后隐藏主窗口
        database = DatabaseUi()
        database.exec()
        # analysis.show()
        # analysis.exec_()
        # self.__analysisUiBuf = True
        database.close()
        self.show()  # 子窗口关闭后重新显示主窗口
        print("数据库选择窗口关闭")

    def __runTextUpdateThread(self):
        self.__textThread = threading.Thread(target=self.__textUpdateThread)
        self.__textThread.setDaemon(True)
        self.__textThread.start()

    #
    # def __runAnalysisUiThread(self):
    #     self.__textThread = threading.Thread(target=self.__analysisUiThread)
    #     self.__textThread.setDaemon(True)
    #     self.__textThread.start()

    def __runCheckSerial(self):
        t = threading.Thread(target=self.__checkSerialThread)
        t.setDaemon(True)  # 设置为守护进程
        t.start()
        # t2 = threading.Thread(target=self.__refreshUiThread)
        # t2.setDaemon(True)
        # t2.start()
        print("监测线程启动")

#
# if __name__ == '__main__':
#     app = QApplication(sys.argv)
#     ex = SetUi()
#     sys.exit(app.exec_())
